Visual Graphics
Namespace: $GG.ui.visual
Related Topics
The Visual Graphics library under the namespace $GG.ui.visual
is designed to facilitate the creation and manipulation of visual elements within the UI framework. This library provides a robust set of tools for handling 2D graphics, including the definition of vertices and orientations, which are fundamental for rendering shapes and animations. It supports a variety of graphical operations essential for building dynamic and interactive user interfaces, ensuring that visual components can be precisely positioned, scaled, and rotated. By integrating seamlessly with the broader UI Library, $GG.ui.visual
allows developers to create visually rich and responsive applications with ease.
Overview
At a high level, the system operates by having a stage in view, with various stage props registered to it. These stage props have coordinates that are local to the stage they belong to. Additionally, stage props can have their own props, creating a hierarchy where each prop's coordinates are relative to its parent. This hierarchical structure can continue indefinitely, allowing for complex nested compositions.
Each object, whether it's a stage or a stage prop, has its own local context to draw onto. The framework calls on each object to render onto its local context, following a specific order: from parent to child, and then by global z-index if there are multiple children. Once an object has rendered its contents onto its local context, that context is copied onto its parent's context in layers using the onDraw
event.
If a parent object or stage needs to draw something on top of its children, it can use the onPostDraw
event to achieve this. This method ensures that the rendering process is flexible and can accommodate complex layering and compositional requirements.
The Clock
The UI features a heartbeat mechanism, often referred to as the clock, which is a fundamental component in a game engine. The clock is responsible for driving the game's update and rendering cycles. It ensures that the game progresses smoothly by periodically calling the onTick methods of all objects in accordance with their hierarchical order within the current stage.
In a game engine, the clock essentially acts as the timing mechanism that governs how frequently game logic and rendering functions are executed. It provides a consistent rhythm to the game's updates and renders, which is crucial for maintaining a smooth and responsive user experience.
The clock also manages the onDraw and onPostDraw event handlers of each object, ensuring they are called in the same order as their hierarchy. This sequence is maintained according to a specified frame rate, which dictates how many times per second these methods are executed. The frame rate can be adjusted dynamically by calling $GG.ui.visual.setFPS(fps: number), allowing developers to optimize performance based on the needs of their application.
Additionally, while the clock can be manually started and stopped, the framework is designed to handle this automatically. Typically, the clock starts running when the engine initializes or when the UI HUD is opened, ensuring that the game loop operates continuously without requiring manual intervention.
Stage Object
Namespace: $GG.ui.visual.type.stages.base
A Stage Object represents a visual screen in an application, containing specific functionalities. Stages manage rendering, event handling, and resizing of components within them. Each stage can be seen as a distinct scene or screen in an application, handling a particular aspect of the application's functionality.
For example, in a gaming application, different stages might represent different levels, menus, or settings screens. In a business application, stages could represent various dashboards or data entry forms.
Constructor
class NewStage extends $GG.ui.visual.type.stages.base {
constructor(name, properties = {}, extended = false) {
super(name, properties, true);
if (!extended) stageManager.registerStage(name, this);
if (this.onInit && !extended) this.onInit(properties);
}
}
- name: The name of the stage. This is a string that identifies the stage.
- properties: An object containing initial properties for the stage. This can include various custom properties defined at initialization.
- extended: A boolean flag used internally to determine whether the super constructor should raise the onInit flag and register the stage, or allow the parent object to handle it. This defaults to
false
.
Properties
id
: A unique identifier.name
: The name of the stage.width
: The width of the stage.height
: The height of the stage.isActive
: A boolean indicating if the object is active.isVisible
: A boolean indicating if the stage is visible.stageProps
: An array of sub-properties or sub-components.eventListeners
: An array of registered event listeners._zIndexTracker
: A tracker for the z-index._zIndex
: The z-index of the object._globalZ
: A string representing the global z-index.
Public Methods
registerProp(instance, properties = {}, priority = 100000)
: Registers a child component (prop) to the stage with optional properties and priority.getProp(id)
: Retrieves a child component by its ID.destroyProp(id)
: Destroys a child component by its ID.clearProps()
: Clears all child components from the stage.zIndex(v = null)
: Gets or sets the z-index of the stage.globalZIndex()
: Calculates the global z-index.resetChildGZ()
: Resets the global z-index for all child components.on(inputEvents, fn)
: Binds custom event listeners to this object.off(inputEvents = null, fn = null)
: Unbinds custom event listeners from this object.trigger(inputEvent, e = {})
: Triggers custom event listeners for this object.
Internal Methods Used By Framework
raiseTickEvent(timeStamp, deltaTime)
: Propagates a tick event to all child components.raiseRenderEvent(context)
: Propagates a render event to all child components.raiseResizeEvent(w, h)
: Propagates a resize event to all child components.raiseStageEvent()
: Triggers the stage event for all child components.raiseUnstageEvent()
: Triggers the unstage event for all child components.raiseStageActiveEvent()
: Triggers the stage active event for all child components.raiseStageInactiveEvent()
: Triggers the stage inactive event for all child components.
Event Handlers
The following event handlers can be implemented by any class that inherits from the base stage object:
- onPreInit(properties): Called before initialization.
- onInit(properties): Called during initialization.
- onTick(timeStamp, deltaTime): Called during each tick event.
- onDraw(context): Called during each render event.
- onPostDraw(context): Called after each render event.
- onResize(w, h): Called during each resize event.
- onStageActive(): Called when the stage becomes active.
- onStageInactive(): Called when the stage becomes inactive.
Portrait and Landscape Stages
PortraitStageType and LandscapeStageType extend the functionality of the base StageType
to enforce specific screen orientations.
- PortraitStageType: Ensures that the stage is always rendered in portrait mode, even on mobile devices. If the device is in landscape orientation, it rotates the canvas to maintain a portrait view.
- LandscapeStageType: Ensures that the stage is always rendered in landscape mode. On mobile devices in portrait orientation, it rotates the canvas to maintain a landscape view.
Both types handle resizing and rendering adjustments to fit the specific orientation, making them suitable for applications that need consistent layout across different devices and orientations.
Prop Object
Constructor
class NewStageProp extends $GG.ui.visual.type.props.base {
constructor(properties = {}, extended = false) {
super(properties, true);
//do something
if (this.onInit && !extended) this.onInit(properties);
}
}
- properties: An object containing initial properties for the stage. This can include various custom properties defined at initialization.
- extended: A boolean flag used internally to determine whether the super constructor should raise the onInit flag and register the stage, or allow the parent object to handle it. This defaults to
false
.
Properties
id
: A unique identifier.name
: The name of the stage.width
: The width of the stage.height
: The height of the stage.isActive
: A boolean indicating if the object is active.isVisible
: A boolean indicating if the stage is visible.stageProps
: An array of sub-properties or sub-components.eventListeners
: An array of registered event listeners._zIndexTracker
: A tracker for the z-index._zIndex
: The z-index of the object._globalZ
: A string representing the global z-index.orientation
: An instance ofOrientation2D
representing the position, scale, and rotation.workCanvas
: An offscreen canvas for intermediate rendering steps.workContext
: The 2D rendering context for the work canvas.screenWidth
: The width of the screen.screenHeight
: The height of the screen._stage
: Reference to stage if attached to a stage._parent
: Reference to parent object if attached.
Orientation2D Structure
The Orientation2D
class is used to manage the position, scale, and rotation of objects in the 2D space. It extends the Vertex2D
class, which handles basic x and y coordinates.
class Vertex2D {
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
class Orientation2D extends Vertex2D {
constructor(x = 0, y = 0, xScale = 1, yScale = 1, rotation = 0) {
super(x, y);
this.rotation = rotation;
this.xScale = xScale;
this.yScale = yScale;
}
}
x
: The x-coordinate of the object, local to its parent from the top left.y
: The y-coordinate of the object, local to its parent from the top left.xScale
: The horizontal scaling factor relative to object width drawn on parent.yScale
: The vertical scaling factor relative to object height drawn on parent.rotation
: The rotation angle in degrees drawn on parent.
Public Methods
registerProp(instance, properties = {}, priority = 100000)
: Registers a child component (prop) to the stage with optional properties and priority.getProp(id)
: Retrieves a child component by its ID.destroyProp(id)
: Destroys a child component by its ID.clearProps()
: Clears all child components from the stage.zIndex(v = null)
: Gets or sets the z-index of the stage.globalZIndex()
: Calculates the global z-index.resetChildGZ()
: Resets the global z-index for all child components.on(inputEvents, fn)
: Binds custom event listeners to this object.off(inputEvents = null, fn = null)
: Unbinds custom event listeners from this object.trigger(inputEvent, e = {})
: Triggers custom event listeners for this object.
Internal Methods Used By Framework
raiseTickEvent(timeStamp, deltaTime)
: Propagates a tick event to all child components.raiseRenderEvent(context)
: Propagates a render event to all child components.raiseResizeEvent(w, h)
: Propagates a resize event to all child components.raiseStageEvent()
: Triggers the stage event for all child components.raiseUnstageEvent()
: Triggers the unstage event for all child components.raiseStageActiveEvent()
: Triggers the stage active event for all child components.raiseStageInactiveEvent()
: Triggers the stage inactive event for all child components.
Event Handlers
The following event handlers can be implemented by any class that inherits from the base stage object:
- onPreInit(properties): Called before initialization.
- onInit(properties): Called during initialization.
- onRegister(properties): Called when a property is registered.
- onTick(timeStamp, deltaTime): Called during each tick event.
- onDraw(context): Called during each render event.
- onPostDraw(context): Called after each render event.
- onResize(w, h): Called during each resize event.
- onStage(): Called when the added to a stage .
- onUnstage(): Called when removed from a stage.
- onStageActive(): Called when the stage becomes active.
- onStageInactive(): Called when the stage becomes inactive.
- onDestroy(): Called when the object is being destroyed.
Sprite and Tileset Props
SpritePropType
The SpritePropType
class extends StagePropType
and is used for managing and rendering animated sprites. It provides functionality to handle sprite animations, frame updates, and drawing the current frame onto the stage. The main features include:
- Frame Management: Controls the current frame, frame rate, and frame duration for the sprite animation.
- Loading Sprites: Loads sprite groups and individual images.
- Drawing Sprites: Draws the appropriate frame onto the canvas based on the current animation state.
TilesetPropType
The TilesetPropType
class extends StagePropType
and is designed to manage and render tilesets, which are essential for grid-based games or maps. It handles the following:
- Tile Initialization: Loads tiles from a predefined layout and initializes them as sprite properties.
- Rendering Tiles: Manages the rendering of each tile on the stage, ensuring proper placement and animation if applicable.
- Layout Management: Handles the layout configuration and resizing of the tileset to fit the stage or screen dimensions.
Both SpritePropType
and TilesetPropType
leverage the base functionality of StagePropType
while adding specific features to handle sprites and tiles efficiently, making them integral parts of the rendering system.
Custom Event Handling
Both stage and stage prop classes provides a robust mechanism for attaching, detaching, and triggering custom events. This is facilitated through the on
, off
, and trigger
methods, allowing for a flexible event-driven architecture.
This provides for easy communications between props and the stages they sit upon.
on(inputEvents, fn)
The on
method is used to bind custom event listeners to an object. It takes two parameters: inputEvents
and fn
.
inputEvents
: A string or an array of strings representing the event names.fn
: The callback function to be executed when the event is triggered.
Example Usage:
const myProp = new StagePropType({});
myProp.on('customEvent', (eventDetails) => {
console.log("Custom event triggered:", eventDetails);
});
Parameters:
inputEvents
(string | string[]): The name of the event(s) to listen for.fn
(function): The callback function to execute when the event is triggered.
Returns:
- void
Errors:
- If
fn
is not a function, an error message is logged to the console.
off(inputEvents = null, fn = null)
The off
method is used to unbind custom event listeners from an object. It takes two optional parameters: inputEvents
and fn
.
inputEvents
: A string or an array of strings representing the event names. If not provided, all event listeners are removed.fn
: The specific callback function to remove. If not provided, all listeners for the specified events are removed.
Example Usage:
// Remove a specific listener
myProp.off('customEvent', myCallbackFunction);
// Remove all listeners for a specific event
myProp.off('customEvent');
// Remove all listeners for all events
myProp.off();
Parameters:
inputEvents
(string | string[] | null): The name of the event(s) to remove listeners from. If null, all events are removed.fn
(function | null): The specific callback function to remove. If null, all listeners for the specified events are removed.
Returns:
- void
`trigger(inputEvent, e =
The trigger
method is used to invoke all listeners attached to a specific event from the prop. It takes two parameters: inputEvent
and e
.
inputEvent
: A string representing the event name.e
: An optional object containing event details to be passed to the callback functions.
Example Usage:
myProp.trigger('customEvent', { key: 'value' });
Parameters:
inputEvent
(string): The name of the event to trigger.e
(object): Optional event details to pass to the event listeners.
Returns:
- void
Practical Example
To illustrate the usage of these methods, consider the following example where a stage attaches to a prop event and the prop triggers it:
// Define a custom stage class extending the base stage class
class MyStage extends $GG.ui.visual.type.stages.base {
// Initialization method called during stage setup
onInit(properties) {
this.initializeStage(); // Initialize the stage and its properties
}
// Method to initialize the stage
initializeStage() {
this.myProp = new StagePropType({ isVisible: true }); // Create a new prop instance
// Attach event listener to the prop
this.myProp.on('customEvent', this.handleCustomEvent.bind(this));
// Register the prop to the stage
this.registerProp(this.myProp);
}
// Event handler for the custom event
handleCustomEvent(eventDetails) {
console.log("Custom event triggered with details:", eventDetails);
}
}
// Define a custom prop class extending the base prop class
class MyProp extends $GG.ui.visual.type.props.base {
// Method called when the prop is registered
onRegister(properties) {
this.triggerEvent(); // Trigger the custom event
}
// Method to trigger the custom event
triggerEvent() {
// Trigger the custom event
this.trigger('customEvent', { key: 'value' });
}
}
Stage Transitions
Stage transitions are crucial in creating smooth and engaging user experiences when moving from one stage or screen to another in applications or games. They provide visual continuity and can help to enhance the user's understanding of the flow between different parts of the application. In our API, we support several types of stage transitions, each with unique visual effects.
Transition Overview
When transitioning between stages, the following state machine is used:
- Idle: No transition is active. The system is in a state where no changes are occurring.
- Started: The transition has been initiated but is not yet active.
- Active: The transition is ongoing, with visual changes being rendered to reflect the transition effect.
Transition Types
1. NoStageTransition
- Description: This transition has no visual effect; it simply switches to the new stage immediately.
- Use Case: Suitable for scenarios where a smooth visual transition is not required or when switching to a new stage quickly.
2. FadeStageTransition
- Description: This transition creates a fading effect, where the current stage fades out while the new stage fades in.
- Attributes:
timeToFade
: Duration of the fade effect in seconds.
- Visual Effect: Gradual change in opacity, with the old stage gradually disappearing and the new stage appearing.
- Use Case: Ideal for creating a smooth and gradual visual transition between stages.
3. SlideStageTransition
- Description: This transition creates a sliding effect, where the current stage slides out of view while the new stage slides into view from the opposite direction.
- Attributes:
timeToSlide
: Duration of the slide effect in seconds.direction
: Direction of the slide (e.g., "left", "right", "top", "bottom").
- Visual Effect: The old stage moves out of view while the new stage moves into view, simulating a sliding motion.
- Use Case: Useful for creating dynamic and directional transitions between stages, especially when the direction of transition is important.
4. ZoomStageTransition
- Description: This transition creates a zoom effect, where the current stage zooms out and shrinks while the new stage zooms in and grows.
- Attributes:
timeToZoom
: Duration of the zoom effect in seconds.
- Visual Effect: The old stage scales down while the new stage scales up, creating a zooming effect.
- Use Case: Ideal for transitions where a visual emphasis on the change in scale is desired.
5. FlipStageTransition
- Description: This transition simulates a flipping effect, where the current stage appears to flip over, revealing the new stage on the other side.
- Attributes:
timeToFlip
: Duration of the flip effect in seconds.direction
: Direction of the flip (e.g., "horizontal", "vertical").
- Visual Effect: The old stage flips over (either horizontally or vertically), transitioning into the new stage.
- Use Case: Suitable for creating a dramatic effect that emphasizes the change in stages through a flipping motion.
Transition Implementation
Each transition class extends the $GG.ui.visual.type.transitions.base
base class and overrides the onDraw
method to implement the specific visual effect of the transition. The raiseTickEvent
and raiseRenderEvent
methods are used to manage the timing and rendering of transitions.
Example
This is the actual code for the fade transition as an example.
class FadeStageTransition extends $GG.ui.visual.type.transitions.base {
constructor(timeToFade) {
super();
this.timeToFade = timeToFade; // Duration of the fade effect in seconds
this.startTime = null; // Time when the transition started
this.opacity = 0; // Initial opacity (0 means fully transparent)
}
setToStage(name) {
this.opacity = 0; // Reset opacity for the new transition
this.startTime = new Date(); // Record the start time of the transition
super.setToStage(name); // Call the base class method to set the new stage
}
onDraw(stage, context) {
if (this.transitionState == $GG.ui.visual.type.transitions.state.Active) {
let timeStamp = new Date(); // Current time for calculating elapsed time
// Calculate the elapsed time since the transition started
let elapsedTime = (timeStamp - this.startTime) / 1000; // Convert ms to seconds
this.opacity = elapsedTime / this.timeToFade; // Update opacity based on elapsed time
// Cap the opacity to a maximum of 1 (fully opaque)
if (this.opacity >= 1) {
this.opacity = 1;
this.transitionState = $GG.ui.visual.type.transitions.state.Idle; // Transition is complete
}
}
if (this.transitionState == $GG.ui.visual.type.transitions.state.Active) {
// Create a new canvas context (newContext) for rendering the new stage
let newCanvas = document.createElement("canvas");
newCanvas.width = this.width;
newCanvas.height = this.height;
let newContext = newCanvas.getContext("2d");
// Render the new stage to the new context
stage.instance.raiseRenderEvent(newContext);
// Draw the old image onto the main context with decreasing opacity
context.globalAlpha = 1 - this.opacity; // Set alpha for old image
context.putImageData(this.fromImage, 0, 0); // Draw the old image
// Draw the new context image onto the main context with increasing opacity
context.globalAlpha = this.opacity; // Set alpha for new image
context.drawImage(newCanvas, 0, 0); // Draw the new image
// Reset globalAlpha to default (fully opaque)
context.globalAlpha = 1;
return;
}
// If the transition is not active, simply render the current stage
stage.instance.raiseRenderEvent(context);
}
}